package plugproxy;

import com.intellij.ui.DocumentAdapter;

import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.border.TitledBorder;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * (c) Copyright 2001,2002 - Brad Wellington
 * =======================================
 * <p/>
 * PlugProxyGui is free software; you can redistribute this file and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * <p/>
 * PlugProxyGui is a UI that wraps around the PlugProxy software.  It provided
 * as a convience tool.
 *
 * @author Christopher A. Longo (cal@cloud9.net)
 * @aauhto Jacky Chan (linux_china@hotmail.com)
 */
public class PlugProxyGui extends JPanel implements DocumentListener {
    private JButton startButton;  //button to start listen
    private JButton clearButton; //button to clear content
    private JTextField hostUI;   //host field
    private JTextField hostPortUI; //host port field
    private JTextField localPortUI;  //local port field
    private JTextArea incomingUI; //incoming text area
    private JTextArea outgoingUI;  //outgoing text area
    private JCheckBox hexModeUI; //hex mode check box
    private JCheckBox bridgeModeUI; //bridge mode ui
    private JCheckBox httpModeUI; // http mode ui
    private JTextField httpFilterUI; //http filter ui
    private JLabel statusLabel;   //status label
    private JCheckBox quietModeUI;   //quiet mode check box
    private JCheckBox lineWrapUI; //line wrap check box
    private boolean isRunning = false;  //running mark
    private PlugProxyListener ppl;  //plugproxy listener
    private Parameters params = new Parameters();  //setting parameters

    /**
     * A default constructor.
     */
    public PlugProxyGui() {
        createUI();
        layoutUI();
        doDocEvent();
    }

    /**
     * fill host, host port and local port information from saved settings
     *
     * @param host      host
     * @param hostPort  host port
     * @param localPort local port
     * @param hexDump   hexDump mode
     */
    public void setSettings(String host, String hostPort, String localPort, String hexDump) {
        this.hostUI.setText(host);
        this.hostPortUI.setText(hostPort);
        this.localPortUI.setText(localPort);
        if (hexDump != null && hexDump.equals("true")) {
            hexModeUI.setSelected(true);
        }
    }

    /**
     * get settings for gui
     *
     * @return information
     */
    public Map<String, String> getSettings() {
        Map<String, String> info = new HashMap<String, String>();
        if (hostUI.getText() != null) {
            info.put("host", hostUI.getText());
        }
        if (hostPortUI.getText() != null) {
            info.put("hostPort", hostPortUI.getText());
        }
        if (localPortUI.getText() != null) {
            info.put("localPort", localPortUI.getText());
        }
        if (hexModeUI.isSelected()) {
            info.put("hexDump", "true");
        }
        if (bridgeModeUI.isSelected()) {
            info.put("bridge", "true");
        }
        if (httpModeUI.isSelected()) {
            info.put("http", "true");
        }
        if (httpFilterUI.getText() != null) {
            info.put("httpFilter", httpFilterUI.getText());
        }
        return info;
    }

    /**
     * For document events.
     *
     * @param event document event
     */
    public void insertUpdate(DocumentEvent event) {
        doDocEvent();
    }

    /**
     * For document events.
     *
     * @param event document event
     */
    public void removeUpdate(DocumentEvent event) {
        doDocEvent();
    }

    /**
     * For document events.
     *
     * @param event document event
     */
    public void changedUpdate(DocumentEvent event) {
        doDocEvent();
    }

    /**
     * create ui for control panel
     */
    private void createUI() {
        startButton = new JButton("Start");
        startButton.addActionListener(new ActionListener() {
            /**
             * start or stop the listener
             * @param event  ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                if (!isRunning) {
                    startListener();
                } else {
                    stopListener();
                }
            }
        });

        clearButton = new JButton("Clear");
        clearButton.addActionListener(new ActionListener() {
            /**
             * clear the incoming and outgoing content
             * @param event ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                incomingUI.setText("");
                outgoingUI.setText("");
            }
        });

        hostUI = new JTextField();
        hostUI.setPreferredSize(new Dimension(240, (int) hostUI.getPreferredSize().getHeight()));
        hostUI.getDocument().addDocumentListener(this);

        hostPortUI = new JTextField();
        hostPortUI.setPreferredSize(new Dimension(75, (int) hostPortUI.getPreferredSize().getHeight()));
        hostPortUI.getDocument().addDocumentListener(this);

        localPortUI = new JTextField();
        localPortUI.setPreferredSize(new Dimension(75, (int) localPortUI.getPreferredSize().getHeight()));
        localPortUI.getDocument().addDocumentListener(this);

        incomingUI = new JTextArea();
//        incomingUI.setFont(new Font("Monospaced", Font.BOLD, 12));
        incomingUI.setEditable(false);

        outgoingUI = new JTextArea();
//        outgoingUI.setFont(new Font("Monospaced", Font.BOLD, 12));
        outgoingUI.setEditable(false);

        hexModeUI = new JCheckBox("Hex Dump");
        hexModeUI.setSelected(false);
        hexModeUI.setOpaque(false);
        hexModeUI.addActionListener(new ActionListener() {
            /**
             * set hex mode or not
             * @param event ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                params.hex = hexModeUI.isSelected();
            }
        });

        bridgeModeUI = new JCheckBox("Bridge");
        bridgeModeUI.setSelected(false);
        bridgeModeUI.setOpaque(false);

        httpModeUI = new JCheckBox(("HTTP"));
        httpModeUI.setToolTipText("HTTP Protocol");
        httpModeUI.setSelected(false);
        httpModeUI.setOpaque(false);
        httpModeUI.addActionListener(new ActionListener() {
            /**
             * set http mode or not
             * @param event  ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                httpFilterUI.setEditable(httpModeUI.isSelected());
                params.http = httpModeUI.isSelected();
            }
        });

        httpFilterUI = new JTextField();
        httpFilterUI.setToolTipText("Wild match, such as *.htm");
        httpFilterUI.setPreferredSize(new Dimension(240, (int) httpFilterUI.getPreferredSize().getHeight()));
        httpFilterUI.setEditable(false);
        httpFilterUI.getDocument().addDocumentListener(new DocumentAdapter() {
            protected void textChanged(DocumentEvent event) {
                params.httpFilter = httpFilterUI.getText();
            }
        });


        quietModeUI = new JCheckBox("Quiet Mode");
        quietModeUI.setSelected(false);
        quietModeUI.setOpaque(false);
        quietModeUI.addActionListener(new ActionListener() {
            /**
             * set quiet mode or not
             * @param event  ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                params.sniff = !quietModeUI.isSelected();
            }
        });

        lineWrapUI = new JCheckBox("Line Wrap");
        lineWrapUI.setSelected(false);
        lineWrapUI.setOpaque(false);
        lineWrapUI.addActionListener(new ActionListener() {
            /**
             * set line wrap or not
             * @param event ActionEvent
             */
            public void actionPerformed(ActionEvent event) {
                incomingUI.setLineWrap(lineWrapUI.isSelected());
                outgoingUI.setLineWrap(lineWrapUI.isSelected());
            }
        });

        statusLabel = new JLabel();
    }

    /**
     * do layout for panel
     */
    private void layoutUI() {
        setLayout(new BorderLayout());
        add(BorderLayout.NORTH, makeTopBar());
        add(BorderLayout.CENTER, makeMessagePanel());
        add(BorderLayout.SOUTH, makeBottomBar());
    }

    /**
     * construct top bar panel to host host, port, local port  field and hex mode check box
     *
     * @return top bar panel
     */
    private JPanel makeTopBar() {
        JPanel panelp = new JPanel();
        panelp.setLayout(new FlowLayout(FlowLayout.LEFT));
        panelp.add(new JLabel("Host "));
        panelp.add(hostUI);
        panelp.add(new JLabel("Remote Port"));
        panelp.add(hostPortUI);
        panelp.add(new JLabel("Local Port "));
        panelp.add(localPortUI);
        panelp.add(httpModeUI);
        panelp.add(httpFilterUI);
        return panelp;
    }

    /**
     * construct split pane for incoming and outgoing panel
     *
     * @return JSplitPane object
     */
    private JSplitPane makeMessagePanel() {
        JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
        splitPane.setOpaque(false);
        splitPane.setDividerSize(5);
        splitPane.setBorder(new EmptyBorder(new Insets(1, 1, 1, 1)));
        splitPane.add(makeOutgoingPanel());
        splitPane.add(makeIncomingPanel());
        return splitPane;
    }

    /**
     * construct bottom bar panel to host quiet mode, line wrap check box and start, stop button
     *
     * @return bottom bar panel
     */
    private JPanel makeBottomBar() {
        JPanel panel = new JPanel();
        panel.setLayout(new BorderLayout());

        JPanel leftPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        leftPanel.add(quietModeUI);
        leftPanel.add(lineWrapUI);
        leftPanel.add(hexModeUI);
//        leftPanel.add(bridgeModeUI);

        JPanel centerPanel = new JPanel(new FlowLayout(FlowLayout.CENTER));
        centerPanel.add(statusLabel);

        JPanel rightPanel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
        rightPanel.add(clearButton);
        rightPanel.add(startButton);

        panel.add(BorderLayout.WEST, leftPanel);
        panel.add(BorderLayout.CENTER, centerPanel);
        panel.add(BorderLayout.EAST, rightPanel);

        return panel;
    }

    /**
     * construct outgoing panel
     *
     * @return outgoing panel
     */
    private JPanel makeOutgoingPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setOpaque(false);
        TitledBorder border = new TitledBorder("Request");
        border.setTitleColor(new Color(65, 65, 65));
        panel.setBorder(border);
        JScrollPane scrollPane = new JScrollPane(outgoingUI);
        //Added in to support the JDK 1.3
        scrollPane.setAutoscrolls(true);
        panel.add(scrollPane, BorderLayout.CENTER);
        panel.setMinimumSize(new Dimension(230, 100));
        panel.setPreferredSize(new Dimension(230, 200));
        return panel;
    }

    /**
     * construct incoming panel
     *
     * @return incoming panel
     */
    private JPanel makeIncomingPanel() {
        JPanel panel = new JPanel(new BorderLayout());
        panel.setOpaque(false);
        TitledBorder border = new TitledBorder("Response");
        border.setTitleColor(new Color(65, 65, 65));
        panel.setBorder(border);
        JScrollPane scrollPane = new JScrollPane(incomingUI);
        scrollPane.setAutoscrolls(true);
        panel.add(scrollPane, BorderLayout.CENTER);
        panel.setMinimumSize(new Dimension(230, 100));
        panel.setPreferredSize(new Dimension(230, 200));
        return panel;
    }

    /**
     * validate parameter setting
     */
    private void doDocEvent() {
        boolean notEmpty = (hostUI.getText().length() > 0) && (hostPortUI.getText().length() > 0) && (localPortUI.getText().length() > 0);
        boolean portIsNumber = true;
        if (notEmpty) {
            try {
                Integer.parseInt(hostPortUI.getText());
                Integer.parseInt(localPortUI.getText());
            }
            catch (NumberFormatException e) {
                portIsNumber = false;
            }
        }
        boolean valid = (notEmpty && portIsNumber);
        startButton.setEnabled(valid);
    }

    /**
     * start listener
     */
    private void startListener() {
        params.host = hostUI.getText();
        if (params.host.contains("(")) {
            params.host = params.host.substring(0, params.host.indexOf("(")).trim();
        }
        params.remotePort = Integer.parseInt(hostPortUI.getText());
        params.localPort = Integer.parseInt(localPortUI.getText());
        params.sniff = true;
        params.hex = hexModeUI.isSelected();
        params.http = httpModeUI.isEnabled();
        params.httpFilter = httpFilterUI.getText();

        statusLabel.setText("Listening.....");
        startButton.setText("Stop ");
        hostUI.setEnabled(false);
        hostPortUI.setEnabled(false);
        localPortUI.setEnabled(false);
        isRunning = true;
        try {
            new Socket(params.host, params.remotePort).close(); // verify host
            ppl = new PlugProxyListener(params);
            ActionListener listener = new ActionListener() {
                /**
                 * action to listen incoming and outgoing , fired by PlugProxyListener 
                 * @param event ActionEvent
                 */
                public void actionPerformed(ActionEvent event) {
                    Date now = new Date();
                    int modifiers = event.getModifiers();
                    long requestedTimestamp = event.getWhen();
                    if ((modifiers & PlugProxyListener.EVENT_DATA) != 0) {
                        if ((modifiers & PlugProxyListener.STREAM_IN) != 0) {
                            if (!incomingUI.getText().contains(requestedTimestamp + "]"))
                                incomingUI.append("[" + now + " # " + requestedTimestamp + "]" + "\r\n");
                            incomingUI.append(event.getActionCommand());
                            int i = incomingUI.getText().length();
                            incomingUI.setCaretPosition(i);
                        } else {
                            if (!outgoingUI.getText().contains(requestedTimestamp + "]"))
                                outgoingUI.append("[" + now + " # " + requestedTimestamp + "]" + "\r\n");
                            outgoingUI.append(event.getActionCommand());
                            int i = outgoingUI.getText().length();
                            outgoingUI.setCaretPosition(i);
                        }
                    }
                }
            };
            ppl.addActionListener(listener);   //add listener to listener socket event
        }
        catch (Exception e) {
            JOptionPane pane = new JOptionPane(e);
            JDialog dialog = pane.createDialog(this, "Error, please press stop button and start again!");
            ((JComponent) dialog.getContentPane().getComponent(0)).setOpaque(false);
            dialog.show();
        }
    }

    /**
     * stop the listener, set related status and stop PlugProxyListener
     */
    private void stopListener() {
        statusLabel.setText("");
        startButton.setText("Start");
        isRunning = false;
        if (ppl != null) {
            ppl.stop();
            ppl = null;
        }
        hostUI.setEnabled(true);
        hostPortUI.setEnabled(true);
        localPortUI.setEnabled(true);
    }

    /**
     * load image from the CLASSPATH
     *
     * @param imageName image name
     * @return Image object
     */
    private Image loadImage(String imageName) {
        Image im = null;
        try {
            im = Toolkit.getDefaultToolkit().createImage(loadResource(imageName));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return im;
    }

    /**
     * Loads a resource from the CLASSPATH
     *
     * @param resourceLocation resource location
     * @return bytes array for resource
     * @throws IOException io exception
     */
    private byte[] loadResource(String resourceLocation) throws IOException {
        BufferedInputStream bis = new BufferedInputStream(getClass().getResourceAsStream(resourceLocation));
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int c;
        while ((c = bis.read()) >= 0) {
            baos.write(c);
        }
        return baos.toByteArray();
    }

}

